Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refine auto-loading behaviour #1915

Merged
merged 8 commits into from
Jul 2, 2024
Merged

Refine auto-loading behaviour #1915

merged 8 commits into from
Jul 2, 2024

Conversation

jennybc
Copy link
Member

@jennybc jennybc commented May 31, 2024

  • Tweak the use of RStudio's session init hook to deliver the intent of PR fix issue with autoloader in RStudio #1652.
  • Otherwise, if RStudio and its session init hook aren't available, just emit a message if we discover the need for renv::restore() during auto-loading.

To do/discuss:

  • After "Installing cli ..." it's easy to think the session is busy or hung. But it's not. If you hit return, you get the normal R prompt. Why does that happen? As I play around with renv, I run into this phenomenon elsewhere. It feels like one or more of the messaging helpers, such as catf() or writef() sometimes fail to get their trailing newline? In any case, I think it's unrelated to what I'm doing here and out of scope for this PR.
  • The "Operation canceled." is unsightly and confusing. Seems like that should be suppressed or, at least in this context, hidden.
  • Try this on: Never try to interact with the user when auto-loading, since user interaction is UB during R startup. If we can defer loading using a hook (like rstudio.sessionInit below), do that. If we can't, emit a message telling the user what to do. If possible, style the renv::restore() part as a runnable link. I had to give up on that part.

@jennybc
Copy link
Member Author

jennybc commented May 31, 2024

Context:

I'm launching an renv-using project, in RStudio, but I just got it from my colleague, e.g. I just git cloned it or similar. So it's clear we're using renv, but the project-specific library doesn't exist yet. While auto-loading, renv wants to interact with me and offer to do renv::restore(), but this happens during R startup, because it's triggered by code in .Rprofile and user interaction is basically undefined behaviour in this context. See this bit in the Note: section:

It is not intended that there be interaction with the user during startup code. Attempting to do so can crash the R process.

https://stat.ethz.ch/R-manual/R-patched/library/base/html/Startup.html

To this end, renv has arrangements to defer loading and any associated user interaction until R is fully initialized, using a hook (added in #1652). But I think the hook isn't currently working as intended.

Before this PR, in RStudio Version 2024.04.2+764, having done renv::init() with renv 1.0.7, I see:

# Bootstrapping renv 1.0.7 ---------------------------------------------------
- Downloading renv ... OK
- Installing renv  ... OK

- Project '~/rrr/renv-coldstart' loaded. [renv 1.0.7]
- None of the packages recorded in the lockfile are currently installed.

Note there is no offer to run renv::restore() (or even specific advice to do so).

After this PR, in RStudio, the interactive nudge to renv::restore() is deferred:

- Project '~/rrr/renv-coldstart' loaded. [renv 1.0.7.9000]
- None of the packages recorded in the lockfile are currently installed.
Would you like to run `renv::restore()` to restore the project library? [y/N]: y

The following package(s) will be updated:

...

After this PR, not in RStudio, there's no attempt to interact but the exact command is given (renv::restore()):

- Project '~/rrr/renv-coldstart' loaded. [renv 1.0.7.9000]
- None of the packages recorded in the lockfile are currently installed.
- Use `renv::restore()` to restore the project library.

# if we're running within RStudio at this point, and we're running
# within the auto-loader, we need to defer execution here so that
# the console is able to properly receive user input and update
# https://github.com/rstudio/renv/issues/1650
autoloading <- getOption("renv.autoloader.running", default = FALSE)
if (autoloading && renv_rstudio_available()) {
setHook("rstudio.sessionInit", function() { renv::load(project) })
setHook("rstudio.sessionInit", function(...) { renv::load(project) })
Copy link
Member Author

@jennybc jennybc May 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before, renv::load(project) was definitely being appended to the "rstudio.sessionInit" hook. I can see that with getHook("rstudio.sessionInit"). However it wasn't actually being executed and I think it must have failed some pre-check re: its signature. In my hands, adding ... makes this feature start to work as intended.

Copy link
Member Author

@jennybc jennybc May 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the explanation:

https://github.com/rstudio/rstudio/blob/8ebe5431ca2fb3c9719b70d4fbc45201d226c79c/src/cpp/session/modules/SessionRHooks.R#L26-L29

RStudio forwards arguments and the whole thing happens inside tryCatch(). So I guess if the hook function does not accept any arguments, the attempt to run the hook fails. Silently.

R/load.R Outdated Show resolved Hide resolved
@@ -881,7 +881,7 @@ renv_load_report_synchronized <- function(project = NULL, lockfile = NULL) {
return(FALSE)
}

response <- ask("Would you like to restore the project library?", default = FALSE)
response <- ask("Would you like to run `renv::restore()` to restore the project library?", default = FALSE)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like this could ease decision-making for the user, i.e. it's easier to answer this question confidently if you know exactly what's being proposed. Also might have some value in terms of user education.

R/load.R Outdated Show resolved Hide resolved
R/load.R Show resolved Hide resolved
R/load.R Outdated Show resolved Hide resolved
Comment on lines -13 to -16
renv_rstudio_autoloading <- function() {
renv_rstudio_available() && getOption("renv.autoloader.running", default = FALSE)
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the sole usage of this function.

Comment on lines +135 to +137
autoloading <- getOption("renv.autoloader.running", default = FALSE)
if (autoloading)
return(default)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More simplification now that we've decided to never attempt interaction when autoloading, regardless of the frontend.

R/load.R Outdated Show resolved Hide resolved
@@ -70,7 +70,8 @@ load <- function(project = NULL, quiet = FALSE, profile = NULL, ...) {

action <- renv_load_action(project)
if (action[[1L]] == "cancel") {
cancel()
autoloading <- getOption("renv.autoloader.running", default = FALSE)
cancel(verbose = !autoloading)
Copy link
Member Author

@jennybc jennybc Jun 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allows us to see the "- Operation canceled." message after overt cancelation by the user, but suppresses such a message when deferring the project load to the session init hook. Discussed elsewhere.

@jennybc jennybc changed the title Refactor some checks when loading Refine auto-loading behaviour Jun 25, 2024
@jennybc jennybc marked this pull request as ready for review June 25, 2024 00:07
@jennybc jennybc requested a review from kevinushey June 25, 2024 00:07
@jennybc
Copy link
Member Author

jennybc commented Jun 25, 2024

This is ready now.

I took a look at adding a test but TBH it looks pretty 😬 . I think this behaviour is legitimately hard to test (interactive usage, with and without RStudio), but I'm also not familiar with all of renv's test helpers, which are extensive. So either this can go forward without an explicit test or else I could use some guidance on how to approach that.

@jennybc
Copy link
Member Author

jennybc commented Jun 26, 2024

If you're interested in getting clickable hyperlinks w/o taking a true cli dependency (I understand why that's a nonstarter), this would be one way:

https://github.com/r-lib/rlang/blob/main/R/standalone-cli.R

I could open a separate issue for that.

Copy link
Collaborator

@kevinushey kevinushey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@kevinushey
Copy link
Collaborator

Do we need a NEWS update? (I'm going to merge this now for testing but we can merge a separate NEWS update after if appropriate)

@kevinushey kevinushey merged commit 65ac9cb into main Jul 2, 2024
11 checks passed
@kevinushey
Copy link
Collaborator

If you're interested in getting clickable hyperlinks w/o taking a true cli dependency (I understand why that's a nonstarter), this would be one way:

https://github.com/r-lib/rlang/blob/main/R/standalone-cli.R

I could open a separate issue for that.

I'd definitely be on board for this.

@jennybc jennybc mentioned this pull request Jul 3, 2024
@jennybc jennybc deleted the bugfix/refactor-load-checks branch July 3, 2024 17:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants